// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#pragma once

#include <map>
#include <memory>
#include <string>

#include <boost/asio.hpp>
#include <nlohmann/json.hpp>

#include "coin2/exchange/base/executor/actor.h"
#include "coin2/exchange/base/executor/archive_executor.h"
#include "coin2/exchange/base/executor/live_executor.h"
#include "coin2/exchange/base/order/order.h"

namespace coin2::strategy::hooks {

namespace impl {

using coin2::exchange::base::executor::ArchiveSyncExecutor;
using coin2::exchange::base::executor::LiveAsyncExecutor;

class PnlMonitor : public IAsyncActor {
 public:
  PnlMonitor(LiveAsyncExecutor* lexec, ArchiveSyncExecutor* aexec, int64_t interval_ns)
      : interval_ns_(interval_ns) {
    CHECK_THROW(lexec != nullptr || aexec != nullptr);  // only one of executor is nullptr

    // TODO(jshin): is it okay to skip shared_ptr?
    timer_ = std::make_unique<boost::asio::steady_timer>(*lexec->MainIoc());
  }

  void Report() {
    // nlohmann::json report;
    // for (auto& [currency, starting_balance] : starting_balance_) {
    //   auto last_balance = last_balance_.at(currency);
    //   report[currency] = {{"start", starting_balance},
    //                       {"current", last_balance},
    //                       {"net", last_balance - starting_balance}};
    // }
    // LOG(INFO) << "\n[PnlMonitor]\n" << report_.dump(4);

    nlohmann::json msg = {
        {"compartment", "strategy"},
        {"component", "PnlMonitor"},
        {"balance", report_},
    };
    LOG(INFO) << msg.dump();

    timer_->expires_after(std::chrono::nanoseconds(interval_ns_));
    timer_->async_wait(std::bind(&PnlMonitor::Report, this));
  }

  void StartAsync() override {
    timer_->expires_after(std::chrono::nanoseconds(interval_ns_));
    timer_->async_wait(std::bind(&PnlMonitor::Report, this));
  }
  void StopAsync() override {}

  void onAccountInfo(const exchange::base::order::OrderUpdate& upd) {
    nlohmann::json table;
    for (const auto& [name, balance] : upd.account_info().GetBalances()) {
      report_[name] = balance.total();
      // CHECK_EQ_THROW(name, balance.currency());
      // if (starting_balance_.find(name) == starting_balance_.end()) {
      //   starting_balance_[name] = balance.total();
      // }
      // last_balance_[name] = balance.total();
    }
  }

 private:
  int64_t interval_ns_;

  std::unique_ptr<boost::asio::steady_timer> timer_;
  nlohmann::json report_;

  // std::map<std::string, double>
  //     starting_balance_;  // balance when this program starts (when OG is ready)
  // std::map<std::string, double>
  //     last_balance_;  // balance when this program starts (when OG is ready)
};

}  // namespace impl

using impl::PnlMonitor;

}  // namespace coin2::strategy::hooks
